#!/usr/bin/perl

use strict;
use Getopt::Std;
use IO::Socket;
use Sys::Hostname;
use POSIX qw(:sys_wait_h);

my $installios_driver= "/usr/sbin/installios_driver";
my ($managed_sys, $netmask, $partition, $client_addr, $res_location, $mac_addr, $profile, $gateway);
my %options;
my $this_host= gethostbyaddr(scalar(gethostbyname(hostname())), AF_INET);
my ($this_addr)= inet_ntoa((gethostbyname($this_host))[4]);
my @args;
my $pid;
my $nimol_log= '/var/log/nimol.log';
my $license_root= '/media/cdrom/nimol/licenses';
my $cd_location= '/media/cdrom/nimol/ioserver_res';
my $restore_location= '/media/cdrom/nim_resources.tar';
my $from_cd= 0;
my $language;
my $accepted= 0;
my $no_net= 0;
my $speed= "100";
my $duplex= "full";
my $debug= "";
my $stripped_mac;

$debug= '-x' if ($ENV{INSTALLIOS_DEBUG});

#are you an hmcsuperadmin?
die "ERROR installios: You must be an hmcsuperadmin to run this command.\n" unless (`lshmcusr --filter "names=\`whoami\`" -F"taskrole" 2>/dev/null` =~ /hmcsuperadmin/);

getopts("s:S:p:i:d:m:r:g:ul:nP:D:", \%options) or die "ERROR installios: usage: installios [-s managed_sys -S netmask -p partition -r profile\n-i client_addr -d source_dir -m mac_addr -g gateway [-P speed] [-D duplex] [-n] [-l language]] | -u\n";

die "ERROR installios: usage: installios [-s managed_sys -S netmask -p partition -r profile\n-i client_addr -d source_dir -m mac_addr -g gateway [-P speed] [-D duplex] [-n] [-l language]] | -u\n" unless ((keys %options) == 0 || $options{u} || $options{s} && $options{S} && $options{p} && $options{i} && $options{d} && $options{m} && $options{r} && $options{g});

&unconfigure if ($options{u});

if ((keys %options) == 0)
{
	my $fields= &wizard;

	$managed_sys= $fields->{managed_system};
	$netmask= $fields->{netmask};
	$partition= $fields->{vio_partition};
	$client_addr= $fields->{client_addr};
	$res_location= $fields->{source};
	$mac_addr= $fields->{mac_addr};
	$profile= $fields->{profile};
	$gateway= $fields->{gateway};
	$no_net= 1 if ($fields->{config_net} eq "n");
	$speed= $fields->{speed} if ($fields->{speed});
	$duplex= $fields->{duplex} if ($fields->{duplex});
	$language= $fields->{language} if ($fields->{language});
}
else
{
	$managed_sys= $options{s};
	$netmask= $options{S};
	$partition= $options{p};
	$client_addr= $options{i};
	$res_location= $options{d};
	$mac_addr= $options{m};
	$profile= $options{r};
	$gateway= $options{g};
	$no_net= 1 if ($options{n});
	$speed= $options{P} if ($options{P});
	$duplex= $options{D} if ($options{D});
}

die "ERROR installios: Only vioserver type partitions may be installed using installios.\n" unless ($ENV{INSTALLIOS_INSTALL_ALL} || `lssyscfg -m \"$managed_sys\" -r lpar --filter \"lpar_names=$partition\" -F\"lpar_type\"` =~ /vioserver/);

$SIG{INT}= \&unconfigure;
$SIG{QUIT}= \&unconfigure;
$SIG{HUP}= 'IGNORE';

if ($res_location eq "/dev/cdrom")
{
	die "ERROR installios: Could not mount /dev/cdrom\n" if (system("/bin/mount /dev/cdrom"));
	$from_cd= 1;
	if (-d $cd_location)
	{
		$res_location= $cd_location;
	}
	else
	{
		$res_location= $restore_location;
	}
	$language= $options{l} if ($options{l});
}

$accepted= &accept_license($language) if ($language);

# run the driver to start NIMOL
push @args, "/usr/sbin/installios_driver";
push @args, "-d";
push @args, "$res_location";
push @args, "-i";
push @args, "$client_addr";
push @args, "-S";
push @args, "$netmask";
push @args, "-m";
push @args, "$mac_addr";
push @args, "-g" ;
push @args, "$gateway";
push @args, "-n" if ($no_net);

if ($accepted)
{
	push @args, "-l";
	push @args, "$language";
}

if ($pid= fork)
{
	#parent
	wait;
}
else
{
	die "ERROR: installios: fork error\n" unless (defined $pid);

	exec(@args) or die "ERROR: installios: Couldn't exec the driver program.\n";
}

#read to the end of the nimol log file
open(NIMOL_LOG, "< $nimol_log") or die "ERROR: installios: could not open the NIMOL log file.\n";
while (<NIMOL_LOG>){}

#now we can exec lpar_netboot
#strip out :

$mac_addr =~ s/://g;

if (system("lpar_netboot $debug -t ent -m $mac_addr -D -s $speed -d $duplex -S $this_addr -G $gateway -C $client_addr \"$partition\" \"$profile\" \"$managed_sys\""))
{
	print STDERR "ERROR: installios: lpar_netboot encountered an error.\n";
	&unconfigure;
}

#at this point monitor the progress of the installation until it has completed
&monitor or die "ERROR: installios: An error occured while monitoring the installation.\n";

sleep 10;

&unconfigure;

sub unconfigure
{
	local $SIG{INT}= 'IGNORE';
	system("/bin/umount /dev/cdrom") if ($from_cd);

	my @args;
	my $pid;

	push @args, "/usr/sbin/installios_driver";
	push @args, "-u";

	if ($pid= fork)
	{
		#parent
		wait;
	}
	else
	{
		die "ERROR: installios: fork error\n" unless (defined $pid);

		exec(@args) or die "ERROR: installios: Couldn't exec the driver program to unconfigure NIMoL.\n";
	}
	exit 0;
}

sub monitor
{
	my $line;
	my $success= 0;
	my $stop_str= '100%';
	my $start_date= scalar(localtime);
	my $reads= 0;
	
	while (1)
	{
		$reads++;

		if ($reads == 5)
		{
			$reads= 0;
			sleep 1;
		}

		next unless ($line= <NIMOL_LOG>);

		system("clear");
		print $start_date."\n-----------$nimol_log :---------------\n";
		print "$line\n";

		if ($line =~ /$stop_str/)
		{
			$success= 1;
			last;
		}
	}
	close NIMOL_LOG;
	$success;
}

#sub interrupt
#{
	#die "installios: Received interrupt signal while installing.  Run installios -u to unconfigure.\n" if ($installing);
#	local $SIG{INT} = 'IGNORE';
#	system("/bin/umount /dev/cdrom") if ($from_cd);
#	&unconfigure;
#}

sub accept_license
{
	my $language= shift;
	my $license_path="$license_root/$language/IOS.la";
	my $return= 0;
	my $response;

	if ($language && -f $license_path)
	{
		system("more $license_path");

		while ($response ne 'y' && $response ne 'n' && $response ne 'Y' && $response ne 'N')
		{
			print "\nDo you accept the license agreement? y/n: ";
			$response= <>;
			chomp $response;
		}

		die "installios: Exiting because license agreement was not accepted.\n" unless ($response eq 'y' || $response eq 'Y');
		$return= 1;
	}
	else
	{
		print STDERR "WARNING: installios: The language \"$language\" is not supported.\n";
	}

	$return;
}

sub wizard
{
	my @types= ('managed_system', 'vio_partition', 'profile');
	my @special_fields_list= ('mac_addr', 'language');
	my @other_fields_list= ('source', 'client_addr', 'netmask', 'gateway', 'speed', 'duplex', 'config_net');
	my %special_fields= ('mac_addr' => \&get_mac_addr,
						 'language' => \&read_license_input);
	my %other_fields= ('client_addr' => [ "Enter the client's intended IP address: ", '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$' ],
					   'netmask' => [ "Enter the client's intended subnet mask: ", '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$' ],
					   'gateway' => [ "Enter the client's gateway: ", '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$' ],
					   'speed' => [ "Enter the client's speed [100]: ", '^10|100|1000|auto$', "100" ],
					   'duplex' => [ "Enter the client's duplex [full]: ", '^full|half|auto$', "full" ],
					   'config_net' => [ "Would you like to configure the client's network after the installation [y]/n?", '^[yY]|[nN]$', "y" ],
					   'source' => [ "Enter the source of the installation images [/dev/cdrom]: ", ".+", "/dev/cdrom" ]);

	#lists determine the order in which the elements are prompted for
	#types list is the hmc-specific data
	#special_fields require special processing
	#other_fields are the regular parameters
	#special_fields require special processing and cannot be handled like the others, so they get their own function

	#other_fields SPEC: msg  ,   string to match   ,   default value

	# main order is types, other_fields, special_fields


	my @elements;
	my %input;
	my %parameters;
	my $i;

	#first go through hmc types needed
	foreach (@types)
	{
		my $cmd;

		if ($_ eq 'managed_system')
		{
			$cmd= 'lssyscfg -r sys -F "name"';
		}
		elsif ($_ eq 'vio_partition')
		{
			$cmd= "lssyscfg -r lpar -m \"$parameters{managed_system}\"".q# -F"name,lpar_type" | /usr/bin/perl -e 'while (<>){chomp;@f= split /,/;print "$f[0]\n" if ($f[1] eq "vioserver");}'#;
		}
		elsif ($_ eq 'profile')
		{
			$cmd= "lssyscfg -r prof -m \"$parameters{managed_system}\" --filter \"lpar_names=".$parameters{vio_partition}."\" -F\"name\"";
		}

		$input{$_}= &get_element($cmd, "\n");
		die "ERROR: installios: No objects of type \"$_\" were found.\n" unless (@{ $input{$_} } > 0);
		$parameters{$_}= &get_input($_, $input{$_});
	}

	#next get all other required fields
	foreach (@other_fields_list)
	{
		my $msg= $other_fields{$_}->[0];
		my $pattern= $other_fields{$_}->[1];
		my $default= $other_fields{$_}->[2];
		my $input_str= "";

		#take everything if no pattern is specified
		$pattern= '.+' unless ($pattern);

		while ($input_str !~ /$pattern/)
		{
			print $msg;
			$input_str= <>;
			chomp $input_str;

			if ((! $input_str) && $default)
			{
				$input_str= $default;
				last;
			}
		}

		$parameters{$_}= $input_str;
	}

	foreach (@special_fields_list)
	{
		$special_fields{$_}->(\%parameters) if ($special_fields{$_});
	}

	print "Here are the values you entered:\n\n";

	foreach (@types,@other_fields_list,@special_fields_list)
	{
		print "$_ = $parameters{$_}\n";
	}

	print "\nPress enter to proceed or type Ctrl-C to cancel...\n";
	$_= <>;

	\%parameters;
}

#calls an hmc command and returns the output in a list reference
# Params:  CMD , DELIM
#          ^     ^
#		cmd to	 delimiter
#		execute
sub get_element
{
	my $cmd= shift;
	my $delim= shift;
	my $output;
	my @ret;

	print "\n";
	$output= `$cmd 2>/dev/null | /usr/bin/sed '/^#.*/d'`;
	
	die "ERROR installios: command '$cmd' failed to execute properly:\n\n$output\n" if ($?);
	@ret= split /$delim/, $output;

	\@ret;
}

sub get_input
{
	my $type= shift;
	my $i= shift;
	my @values= @$i;
	my $max= @values;
	$i= 0;

	#my %input= (managed_system => "",
	#			partition => "",
	#			profile => "");

	die "ERROR: installios: No objects or type $type were found.\n" unless ($max > 0);

	print "The following objects of type \"$type\" were found.  Please select one:\n\n";

	foreach (@values)
	{
		print ++$i.". $_\n";
	}
	$i= -1;
	print "\n";
	my $range;
	$range= " (1-$max)" if (@values > 1);

	while (!($i =~ /\d+/) || $i < 1 || $i > @values)
	{
		print "Enter a number".$range.": ";
		$i= <>;
		chomp $i;
	}

	$values[$i - 1];
}

sub get_mac_addr
{
	my $fields= shift;
	my $line;
	my $type= 'mac_addr';
	
	print "\nRetrieving information for available network adapters\nThis will take several minutes...\n\n";
	$line= &get_input($type, &get_element("lpar_netboot -n -M -A -t ent \"$fields->{vio_partition}\" \"$fields->{profile}\" \"$fields->{managed_system}\"", "\n"));

	$fields->{$type}= (split /\s+/, $line)[2];
    $fields->{$type} =~ s/([0-9a-f]{2})/$1:/g;
    chop $fields->{$type};
}

sub read_license_input
{
	my $fields= shift;
	my $line;
	my $type= 'language';

	if ($fields->{source} eq '/dev/cdrom')
	{
		print "Enter a language in which to view the license agreement,\nor leave blank and press enter to view the agreement later: ";
		$fields->{$type}= <>;
		chomp $fields->{$type};
	}
}
